package com.jlee.android.pimclient.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

import android.app.Service;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.provider.CallLog;
import android.provider.Contacts;
import android.util.Log;

import com.jlee.android.pimclient.PIMClientConstant;

public class PIMClientService extends Service {
	private Cursor mContactOrganizationCursor;
	private Cursor mContactPeopleCursor;
	private Cursor mContactMethodsCursor;
	private Cursor mContactPhoneCursor;
	private Cursor mContactPhotoCursor;
	private Cursor mCallLogCursor;
	private Cursor mSMSCursor;
	private Cursor mCalendarCursor;

	private HashMap<Integer, ContactInfo> mContactInfoMap;
	private ArrayList<CallLogInfo> mCallLogInfoList;
	private ArrayList<SMSInfo> mSMSInfoList;
	private ArrayList<CalendarInfo> mCalendarInfoList;
	
	// FIXME: This is public for Test purpose, later should find
	//		  a better way to build the test.
	public PIMClientContentObserver mContentObserver;
	
	private ContentResolver mContentResolver;
	
	@Override
	public void onCreate() {
		super.onCreate();

		Log.i(PIMClientConstant.TAG, "PIM Client Service started.");
		
		mContentResolver = getContentResolver();
		
		initCursors();
		checkIsFirstStart();
	}

	private void initCursors() {
		mContactInfoMap = new HashMap<Integer, ContactInfo>();
		mCallLogInfoList = new ArrayList<CallLogInfo>();
		mSMSInfoList = new ArrayList<SMSInfo>();
		mCalendarInfoList = new ArrayList<CalendarInfo>();
		
		mContentObserver = new PIMClientContentObserver();

		initContactCursors();
		initCallLogCursor();
		initSMSCursor();
		initCalendarCursor();
	}

	private void initContactCursors() {
		initOrganizationCursor();
		initPeopleCursor();
		initContactMethodsCursor();
		initPhoneCursor();
		initPhotoCursor();
	}
	
	private void updateContactInfo(ContactRecorder contactRecorder) {
		mContactInfoMap.clear();

		updateOrganizationInfo(true);
		updatePeopleInfo(true, contactRecorder);
		updateContactMethodsInfo(true);
		updatePhoneInfo(true);
		updatePhotoInfo(true);
	}
	
	private void initCallLogCursor() {
		mCallLogCursor = mContentResolver.query(
				CallLog.Calls.CONTENT_URI, 
				PIMClientConstant.CALLLOG_PROJECTION, null, null, null);
		updateCallLogInfo(false);
		
		mCallLogCursor.registerContentObserver(mContentObserver);
	}
	
	private void initSMSCursor() {
		mSMSCursor = mContentResolver.query(
				PIMClientConstant.SMS_CONTENT_URI, 
				PIMClientConstant.SMS_PROJECTION, null, null, null);
		updateSMSInfo(false);
		
		mSMSCursor.registerContentObserver(mContentObserver);
	}

	private void initCalendarCursor() {
		mCalendarCursor = mContentResolver.query(
				PIMClientConstant.CALENDAR_CONTENT_URI, 
				PIMClientConstant.CALENDAR_PROJECTION, null, null, null);
		updateCalendarInfo(false);
		
		mCalendarCursor.registerContentObserver(mContentObserver);
	}

	private void initPhotoCursor() {
		mContactPhotoCursor = mContentResolver.query(
				Contacts.Photos.CONTENT_URI, 
				PIMClientConstant.CONTACT_PHOTOS_PROJECTION, null, null, null);
		updatePhotoInfo(false);
		
		mContactPhotoCursor.registerContentObserver(mContentObserver);
	}

	private void initPhoneCursor() {
		mContactPhoneCursor = mContentResolver.query(
				Contacts.Phones.CONTENT_URI, 
				PIMClientConstant.CONTACT_PHONE_PROJECTION, null, null, null);
		updatePhoneInfo(false);
		
		mContactPhoneCursor.registerContentObserver(mContentObserver);
	}

	private void initContactMethodsCursor() {
		mContactMethodsCursor = mContentResolver.query(
				Contacts.ContactMethods.CONTENT_URI, 
				PIMClientConstant.CONTACT_METHODS_PROJECTION, null, null, null);
		updateContactMethodsInfo(false);
		
		mContactMethodsCursor.registerContentObserver(mContentObserver);
	}

	private void initPeopleCursor() {
		mContactPeopleCursor = mContentResolver.query(
				Contacts.People.CONTENT_URI,
				PIMClientConstant.CONTACT_PEOPLE_PROJECTION, null, null, null);
		updatePeopleInfo(false, null);
		
		mContactPeopleCursor.registerContentObserver(mContentObserver);
	}
	
	void updateCalendarInfo(boolean needRequery) {
		if (mCalendarCursor == null) {
			return;
		}
		
		if (needRequery) {
			mCalendarCursor.requery();
		}
		
		mCalendarInfoList.clear();
		while (mCalendarCursor.moveToNext()) {
			String title = mCalendarCursor.getString(
					PIMClientConstant.CALENDAR_TITLE_INDEX);
			String description = mCalendarCursor.getString(
					PIMClientConstant.CALENDAR_DESCRIPTIOOIN_INDEX);
			String location = mCalendarCursor.getString(
					PIMClientConstant.CALENDAR_LOCATION_INDEX);
			long startTime = mCalendarCursor.getLong(
					PIMClientConstant.CALENDAR_STARTTIME_INDEX);
			long endTime = mCalendarCursor.getLong(
					PIMClientConstant.CALENDAR_ENDTIME_INDEX);
			
			CalendarInfo calendarInfo = new CalendarInfo();
			calendarInfo.mTitle = title;
			calendarInfo.mDescription = description;
			calendarInfo.mLocation = location;
			calendarInfo.mStartTime = new Date(startTime).toGMTString();
			calendarInfo.mEndTime = new Date(endTime).toGMTString();
			
			mCalendarInfoList.add(calendarInfo);
		}
	}

	void updateSMSInfo(boolean needRequery) {
		if (mSMSCursor == null) {
			return;
		}
		
		if (needRequery) {
			mSMSCursor.requery();
		}
		
		mSMSInfoList.clear();
		while (mSMSCursor.moveToNext()) {
			long date = mSMSCursor.getLong(PIMClientConstant.SMS_DATE_INDEX);
			String body = mSMSCursor.getString(PIMClientConstant.SMS_BODY_INDEX);
			int type = mSMSCursor.getInt(PIMClientConstant.SMS_TYPE_INDEX);
			String number = mSMSCursor.getString(PIMClientConstant.SMS_ADDRESS_INDEX);
	
			String typeStr;
			switch (type) {
			case PIMClientConstant.SMS_TYPE_INBOX:
				typeStr = "received";
				break;
			case PIMClientConstant.SMS_TYPE_SENT:
				typeStr = "sent";
				break;
			default:
				typeStr = "other";
				break;
			}
			SMSInfo smsInfo = new SMSInfo();
			smsInfo.mDate = new Date(date).toGMTString();
			smsInfo.mBody = body;
			smsInfo.mType = typeStr;
			smsInfo.mNumber = number;
			
			mSMSInfoList.add(smsInfo);
		}
	}
	
	void updateCallLogInfo(boolean needRequery) {
		if (mCallLogCursor == null) {
			return;
		}
		
		if (needRequery) {
			mCallLogCursor.requery();
		}
		
		mCallLogInfoList.clear();
		while (mCallLogCursor.moveToNext()) {
			String name = mCallLogCursor.getString(
					PIMClientConstant.CALLLOG_NAME_INDEX);
			String number = mCallLogCursor.getString(
					PIMClientConstant.CALLLOG_NUMBER_INDEX);
			int type = mCallLogCursor.getInt(
					PIMClientConstant.CALLLOG_TYPE_INDEX);
			long duration = mCallLogCursor.getLong(
					PIMClientConstant.CALLLOG_DURATION_INDEX);
			
			CallLogInfo logInfo = new CallLogInfo();
			logInfo.mCallName = (name == null ? "" : name);
			logInfo.mCallNumber = (number == null ? "" : number);
			logInfo.mDuration = duration;
			switch (type) {
			case CallLog.Calls.INCOMING_TYPE:
				logInfo.mType = "incoming";
				break;
			case CallLog.Calls.OUTGOING_TYPE:
				logInfo.mType = "outgoing";
				break;
			case CallLog.Calls.MISSED_TYPE:
				logInfo.mType = "missed";
				break;
			default:
				break;
			}
			
			mCallLogInfoList.add(logInfo);
		}
	}
	
	void updatePhotoInfo(boolean needRequery) {
		if (mContactPhotoCursor == null) {
			return;
		}
		
		if (needRequery) {
			mContactPhotoCursor.requery();
		}
		
		while (mContactPhotoCursor.moveToNext()) {
			int id = mContactPhotoCursor.getInt(
					PIMClientConstant.CONTACT_PHOTOS_PERSONID_INDEX);
			String data = mContactPhotoCursor.getString(
					PIMClientConstant.CONTACT_PHOTOS_DATA_INDEX);
			
			ContactInfo cInfo = getContactInfo(id);
			cInfo.mPicture = (data == null ? "" : data);
		}
	}
	
	void updatePhoneInfo(boolean needRequery) {
		if (mContactPhoneCursor == null) {
			return;
		}
		
		if (needRequery) {
			mContactPhoneCursor.requery();
		}
		
		while (mContactPhoneCursor.moveToNext()) {
			int id = mContactPhoneCursor.getInt(
					PIMClientConstant.CONTACT_PHONE_PERSONID_INDEX);
			int type = mContactPhoneCursor.getInt(
					PIMClientConstant.CONTACT_PHONE_TYPE_INDEX);
			String number = mContactPhoneCursor.getString(
					PIMClientConstant.CONTACT_PHONE_NUMBER_INDEX);
			
			number = (number == null ? "" : number); 
			ContactInfo cInfo = getContactInfo(id);
			switch (type) {
			case Contacts.Phones.TYPE_MOBILE:
				cInfo.mMobile = number;
				break;
			case Contacts.Phones.TYPE_HOME:
				cInfo.mHomePhone = number;
				break;
			case Contacts.Phones.TYPE_WORK:
				cInfo.mWorkPhone = number;
				break;
			case Contacts.Phones.TYPE_FAX_HOME:
				cInfo.mHomeFax = number;
				break;
			case Contacts.Phones.TYPE_FAX_WORK:
				cInfo.mWorkFax = number;
				break;
			default:
				break;
			}
		}
	}

	void updateContactMethodsInfo(boolean needRequery) {
		if (mContactMethodsCursor == null) {
			return;
		}
		
		if (needRequery) {
			mContactMethodsCursor.requery();
		}
		
		while (mContactMethodsCursor.moveToNext()) {
			int id = mContactMethodsCursor.getInt(
					PIMClientConstant.CONTACT_METHODS_PERSONID_INDEX);
			String data = mContactMethodsCursor.getString(
					PIMClientConstant.CONTACT_METHODS_DATA_INDEX);
			int type = mContactMethodsCursor.getInt(
					PIMClientConstant.CONTACT_METHODS_TYPE_INDEX);
			int kind = mContactMethodsCursor.getInt(
					PIMClientConstant.CONTACT_METHODS_KIND_INDEX);
			
			data = (data == null ? "" : data);
			ContactInfo cInfo = getContactInfo(id);
			if (kind == Contacts.KIND_EMAIL) {
				if (type == Contacts.ContactMethods.TYPE_HOME) {
					cInfo.mPersonalMail = data;
				} else if (type == Contacts.ContactMethods.TYPE_WORK) {
					cInfo.mBusinessMail = data;
				}
			} else if (kind == Contacts.KIND_IM) {
				cInfo.mIm = data;
			}
		}
	}
	
	void updatePeopleInfo(boolean needRequery, ContactRecorder contactRecorder) {
		if (mContactPeopleCursor == null) {
			return;
		}

		if (needRequery) {
			mContactPeopleCursor.requery();
		}

		while (mContactPeopleCursor.moveToNext()) {
			int id = mContactPeopleCursor.getInt(
					PIMClientConstant.CONTACT_PEOPLE_ID_INDEX);
			String name = mContactPeopleCursor.getString(
					PIMClientConstant.CONTACT_PEOPLE_NAME_INDEX);
			String displayName = mContactPeopleCursor.getString(
					PIMClientConstant.CONTACT_PEOPLE_DISPLAYNAME_INDEX);
			String notes = mContactPeopleCursor.getString(
					PIMClientConstant.CONTACT_PEOPLE_NOTES_INDEX);
			
			ContactInfo cInfo = getContactInfo(id);
			cInfo.mName = (name == null ? "" : name);
			cInfo.mDisplayName = (displayName == null ? "" : displayName);
			cInfo.mNotes = (notes == null ? "" : notes);

			if (contactRecorder != null) {
				contactRecorder.recordName(cInfo.mName);
			}
		}	
	}
	
	void updateOrganizationInfo(boolean needRequery) {
		if (mContactOrganizationCursor == null) {
			return;
		}

		if (needRequery) {
			mContactOrganizationCursor.requery();
		}

		while (mContactOrganizationCursor.moveToNext()) {
			int personId = mContactOrganizationCursor.getInt(
					PIMClientConstant.CONTACT_ORGANIZATION_PERSONID_INDEX);
			String companyName = mContactOrganizationCursor.getString(
					PIMClientConstant.CONTACT_ORGANIZATION_COMPANYNAME_INDEX);
			String jobTitle = mContactOrganizationCursor.getString(
					PIMClientConstant.CONTACT_ORGANIZATION_JOBTITLE_INDEX);
			
			ContactInfo cInfo = getContactInfo(personId);
			cInfo.mCompanyName = (companyName == null ? "" : companyName);
			cInfo.mJobTitle = (jobTitle == null ? "" : jobTitle);
		}
	}

	private void initOrganizationCursor() {
		mContactOrganizationCursor = mContentResolver.query(
				Contacts.Organizations.CONTENT_URI, 
				PIMClientConstant.CONTACT_ORGANIZATION_PROJECTION, null, null, null);
		updateOrganizationInfo(false);

		mContactOrganizationCursor.registerContentObserver(mContentObserver);		
	}

	public final class PIMClientContentObserver extends ContentObserver {
		private ContactRecorder mContactRecorder;

		public PIMClientContentObserver() {
			super(new Handler());
		}

		@Override
		public void onChange(boolean selfChange) {
			Log.i(PIMClientConstant.TAG, "On database changed");
			//add unite test by xiaomei
			updateContactInfo(mContactRecorder);
			updateCallLogInfo(true);
			updateSMSInfo(true);
			updateCalendarInfo(true);

			try {
				// FIXME: Currently will export all the info the the xml file
				saveData();
			} catch (IOException e) {
				Log.e(PIMClientConstant.TAG, 
						"Got IOException while save on organization change", e);
			}
		}

		public void setContactRecorder(ContactRecorder recorder) {
			mContactRecorder = recorder;
		}
	}
	
	private ContactInfo getContactInfo(int personId) {
		if (mContactInfoMap.containsKey(personId)) {
			return mContactInfoMap.get(personId);
		}

		ContactInfo cInfo = new ContactInfo(personId);
		mContactInfoMap.put(personId, cInfo);
		return cInfo;
	}
	
	private static final class CalendarInfo {
		String mTitle;
		String mDescription;
		String mLocation;
		String mStartTime;
		String mEndTime;
	}
	
	private static final class SMSInfo {
		String mDate;
		String mBody;
		String mType;
		String mNumber;
	}
	
	private static final class CallLogInfo {
		String mCallName;
		String mCallNumber;
		long mDuration;
		String mType;
	}

	private static final class ContactInfo {
		int mId;
		String mIm = "";
		String mName = "";
		String mDisplayName = "";
		String mNotes = "";
		String mCompanyName = "";
		String mJobTitle = "";
		String mPersonalMail = "";
		String mBusinessMail = "";
		String mMobile = "";
		String mHomePhone = "";
		String mWorkPhone = "";
		String mHomeFax = "";
		String mWorkFax = "";
		String mPicture = "";
		
		ContactInfo(int id) {
			mId = id;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	private void checkIsFirstStart() {
		SharedPreferences settings = getSharedPreferences(
				PIMClientConstant.PREFS_NAME, MODE_PRIVATE);
		if (!settings.contains(PIMClientConstant.PREFS_KEY_FIRSTSTART)) {
			Log.i(PIMClientConstant.TAG, "Is first start");
			doFirstTimeSave(settings);
		} else {
			Log.i(PIMClientConstant.TAG, "Not first start");
		}
	}
	
	private void doFirstTimeSave(SharedPreferences settings) {
		SharedPreferences.Editor editor = settings.edit();
		editor.putBoolean(PIMClientConstant.PREFS_KEY_FIRSTSTART, true);

		editor.commit();
		
		try {
			saveData();
		} catch (IOException e) {
			Log.e(PIMClientConstant.TAG, "Got IOException while do first time save", e);
		}
	}

	void saveData() throws IOException {
		Log.i(PIMClientConstant.TAG, "Saving data...");

    	File sdcard = Environment.getExternalStorageDirectory();
    	File pimFile = new File(sdcard, PIMClientConstant.XML_PATH);
    	
    	OutputStream os = new FileOutputStream(pimFile);
    	
    	SharedPreferences settings = getSharedPreferences(
    			PIMClientConstant.PREFS_NAME, MODE_PRIVATE);
    	
    	writeString(os, "<pim>\n");
    	
    	// write contact info
    	for (int key : mContactInfoMap.keySet()) {
    		ContactInfo contactInfo = mContactInfoMap.get(key);

    		StringBuilder buf = new StringBuilder();
    		buf.append("\t<contact");
    		
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_COMPANYNAME, true))
    			buf.append(" companyname=\"" + contactInfo.mCompanyName + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_JOBTITLE, true))
    			buf.append(" jobtitle=\"" + contactInfo.mJobTitle + "\"");
    		
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_NAME, true)) {
    			String name = contactInfo.mName;
    			String firstName = "";
    			String middleName = "";
    			String lastName = "";

    			if (-1 == name.indexOf(' ')) {
    				// Only one name
    				firstName = name;
    			} else if (name.indexOf(' ') == name.lastIndexOf(' ')) {
    				// First name and Last name
    				firstName = name.substring(0, name.indexOf(' '));
    				lastName = name.substring(name.lastIndexOf(' ') + 1, name.length());
    			} else {
    				firstName = name.substring(0, name.indexOf(' '));
    				middleName = name.substring(name.indexOf(' ') + 1, name.lastIndexOf(' ')); 
    				lastName = name.substring(name.lastIndexOf(' ') + 1, name.length());
    			}
    			
    			buf.append(" firstname=\"" + firstName + "\"");
    			buf.append(" middlename=\"" + middleName + "\"");
    			buf.append(" lastname=\"" + lastName + "\"");
    		}    
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_DISPLAYNAME, true))
    			buf.append(" displayname=\"" + contactInfo.mDisplayName + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_NOTES, true))
    			buf.append(" notes=\"" + contactInfo.mNotes + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_IM, true))
    			buf.append(" im=\"" + contactInfo.mIm + "\"");
    		
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_PERSONALMAIL, true))
    			buf.append(" personalmail=\"" + contactInfo.mPersonalMail + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_BUSINESSMAIL, true))
    			buf.append(" businessmail=\"" + contactInfo.mBusinessMail + "\"");
    		
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_MOBILE, true))
    			buf.append(" mobile=\"" + contactInfo.mMobile + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_HOMEPHONE, true))
    			buf.append(" homephone=\"" + contactInfo.mHomePhone + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_WORKPHONE, true))
    			buf.append(" workphone=\"" + contactInfo.mWorkPhone + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_HOMEFAX, true))
    			buf.append(" homefax=\"" + contactInfo.mHomeFax + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_WORKFAX, true))
    			buf.append(" workfax=\"" + contactInfo.mWorkFax + "\"");
    		
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CONTACT_PICTURE, true))
    			buf.append(" picturedata=\"" + contactInfo.mPicture + "\"");

    		buf.append("/>\n");
    		
    		writeString(os, buf.toString());
    	}
    	
    	// write call log information
    	for (CallLogInfo logInfo : mCallLogInfoList) {
    		StringBuilder buf = new StringBuilder();
    		
    		buf.append("\t<loginfo");

    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALLLOG_NAME, true))
    			buf.append(" name=\"" + logInfo.mCallName + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALLLOG_NUMBER, true))
    			buf.append(" number=\"" + logInfo.mCallNumber + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALLLOG_TYPE, true))
    			buf.append(" type=\"" + logInfo.mType + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALLLOG_DURATION, true))
    			buf.append(" duration=\"" + logInfo.mDuration + "\"");

    		buf.append("/>\n");
    		
    		writeString(os, buf.toString());
    	}
    	
    	// write sms information
    	for (SMSInfo smsInfo : mSMSInfoList) {
    		StringBuilder buf = new StringBuilder();
    		
    		buf.append("\t<sms");
    		
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_SMS_DATE, true))
    			buf.append(" date=\"" + smsInfo.mDate + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_SMS_MESSAGE, true))
    			buf.append(" body=\"" + smsInfo.mBody + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_SMS_TYPE, true))
    			buf.append(" type=\"" + smsInfo.mType + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_SMS_NUMBER, true))
    			buf.append(" number=\"" + smsInfo.mNumber + "\"");

    		buf.append("/>\n");
    		
    		writeString(os, buf.toString());
    	}
    	
    	// write calendar information
    	for (CalendarInfo calendarInfo : mCalendarInfoList) {
    		StringBuilder buf = new StringBuilder();
    		
    		buf.append("\t<calendar");

    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALENDAR_TITLE, true))
    			buf.append(" title=\"" + calendarInfo.mTitle + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALENDAR_DESCRIPTION, true))
    			buf.append(" description=\"" + calendarInfo.mDescription + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALENDAR_LOCATION, true))
    			buf.append(" location=\"" + calendarInfo.mLocation + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALENDAR_STARTTIME, true))
    			buf.append(" starttime=\"" + calendarInfo.mStartTime + "\"");
    		if (settings.getBoolean(PIMClientConstant.PREFS_KEY_CALENDAR_ENDTIME, true))
    			buf.append(" endtime=\"" + calendarInfo.mEndTime + "\"");

    		buf.append("/>\n");
    		
    		writeString(os, buf.toString());    		
    	}
    	
    	writeString(os, "</pim>\n");
	}
	
    void writeString(OutputStream os, String str) throws IOException {
    	os.write(str.getBytes());
    }
}
